"""
高性能缓存集成DAO
将多级缓存策略深度集成到数据访问层
"""

import json
import hashlib
from typing import List, Optional, Dict, Any, Type, TypeVar, Generic, Union
from datetime import datetime, date, timedelta
from functools import wraps
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, desc, asc, func

from .base import BaseDAO, FinancialDataDAO
from ..core.cache import CacheManager, CacheLevel, CacheStrategy
from ..core.database import DatabaseManager
from ..core.config import get_settings

ModelType = TypeVar('ModelType')

class CachedDAOConfig:
    """缓存DAO配置"""
    
    def __init__(
        self,
        default_ttl: int = 300,  # 5分钟默认TTL
        cache_levels: List[CacheLevel] = None,
        cache_strategy: CacheStrategy = CacheStrategy.LRU,
        enable_write_through: bool = True,
        enable_query_cache: bool = True,
        enable_result_cache: bool = True,
        max_cache_size: int = 10000,
        cache_key_prefix: str = "dao"
    ):
        self.default_ttl = default_ttl
        self.cache_levels = cache_levels or [CacheLevel.L1_MEMORY, CacheLevel.L2_REDIS]
        self.cache_strategy = cache_strategy
        self.enable_write_through = enable_write_through
        self.enable_query_cache = enable_query_cache
        self.enable_result_cache = enable_result_cache
        self.max_cache_size = max_cache_size
        self.cache_key_prefix = cache_key_prefix


class CacheKeyGenerator:
    """缓存键生成器"""
    
    @staticmethod
    def generate_key(
        model_name: str,
        method_name: str,
        args: tuple = (),
        kwargs: dict = None
    ) -> str:
        """生成缓存键"""
        kwargs = kwargs or {}
        
        # 创建参数字符串
        params = []
        for arg in args:
            if isinstance(arg, (date, datetime)):
                params.append(arg.isoformat())
            elif isinstance(arg, (int, float, str, bool)):
                params.append(str(arg))
            else:
                params.append(str(type(arg).__name__))
        
        for key, value in sorted(kwargs.items()):
            if isinstance(value, (date, datetime)):
                params.append(f"{key}={value.isoformat()}")
            elif isinstance(value, (int, float, str, bool)):
                params.append(f"{key}={value}")
            elif value is not None:
                params.append(f"{key}={type(value).__name__}")
        
        param_str = "&".join(params)
        
        # 生成哈希
        key_data = f"{model_name}:{method_name}:{param_str}"
        key_hash = hashlib.md5(key_data.encode()).hexdigest()[:16]
        
        return f"dao:{model_name}:{method_name}:{key_hash}"
    
    @staticmethod
    def generate_entity_key(model_name: str, entity_id: Any) -> str:
        """生成实体缓存键"""
        return f"entity:{model_name}:{entity_id}"
    
    @staticmethod
    def generate_query_key(model_name: str, query_hash: str) -> str:
        """生成查询缓存键"""
        return f"query:{model_name}:{query_hash}"


def cache_result(
    ttl: Optional[int] = None,
    cache_levels: Optional[List[CacheLevel]] = None,
    cache_strategy: Optional[CacheStrategy] = None,
    key_generator: Optional[callable] = None
):
    """缓存结果装饰器"""
    def decorator(func):
        @wraps(func)
        async def wrapper(self, *args, **kwargs):
            if not hasattr(self, 'cache_manager') or not self.cache_config.enable_result_cache:
                return await func(self, *args, **kwargs)
            
            # 生成缓存键
            if key_generator:
                cache_key = key_generator(self, *args, **kwargs)
            else:
                cache_key = CacheKeyGenerator.generate_key(
                    self.model_class.__name__,
                    func.__name__,
                    args[1:],  # 跳过self参数
                    kwargs
                )
            
            # 尝试从缓存获取
            cached_result = await self.cache_manager.get(cache_key)
            if cached_result is not None:
                self._update_cache_stats('hit', func.__name__)
                return self._deserialize_result(cached_result)
            
            # 执行原函数
            result = await func(self, *args, **kwargs)
            
            # 缓存结果
            if result is not None:
                serialized_result = self._serialize_result(result)
                await self.cache_manager.set(
                    cache_key,
                    serialized_result,
                    ttl=ttl or self.cache_config.default_ttl,
                    levels=cache_levels or self.cache_config.cache_levels,
                    strategy=cache_strategy or self.cache_config.cache_strategy
                )
            
            self._update_cache_stats('miss', func.__name__)
            return result
        
        return wrapper
    return decorator


class CachedBaseDAO(BaseDAO[ModelType]):
    """带缓存的基础DAO"""
    
    def __init__(
        self,
        session: AsyncSession,
        model_class: Type[ModelType],
        cache_manager: CacheManager,
        cache_config: CachedDAOConfig = None
    ):
        super().__init__(session, model_class)
        self.cache_manager = cache_manager
        self.cache_config = cache_config or CachedDAOConfig()
        self.cache_stats = {
            'hits': 0,
            'misses': 0,
            'writes': 0,
            'invalidations': 0
        }
    
    def _serialize_result(self, result: Any) -> str:
        """序列化结果"""
        if isinstance(result, list):
            return json.dumps([self._model_to_dict(item) for item in result])
        elif hasattr(result, '__dict__'):
            return json.dumps(self._model_to_dict(result))
        else:
            return json.dumps(result)
    
    def _deserialize_result(self, data: str) -> Any:
        """反序列化结果"""
        try:
            parsed_data = json.loads(data)
            if isinstance(parsed_data, list):
                return [self._dict_to_model(item) for item in parsed_data]
            elif isinstance(parsed_data, dict) and 'id' in parsed_data:
                return self._dict_to_model(parsed_data)
            else:
                return parsed_data
        except (json.JSONDecodeError, KeyError):
            return None
    
    def _model_to_dict(self, model: ModelType) -> Dict[str, Any]:
        """模型转字典"""
        result = {}
        for column in model.__table__.columns:
            value = getattr(model, column.name)
            if isinstance(value, (date, datetime)):
                result[column.name] = value.isoformat()
            elif isinstance(value, (int, float, str, bool)) or value is None:
                result[column.name] = value
            else:
                result[column.name] = str(value)
        return result
    
    def _dict_to_model(self, data: Dict[str, Any]) -> ModelType:
        """字典转模型"""
        # 这里需要根据实际模型类型进行转换
        # 简化实现，实际应用中需要更复杂的转换逻辑
        instance = self.model()
        for key, value in data.items():
            if hasattr(instance, key):
                setattr(instance, key, value)
        return instance
    
    def _update_cache_stats(self, operation: str, method: str):
        """更新缓存统计"""
        if operation == 'hit':
            self.cache_stats['hits'] += 1
        elif operation == 'miss':
            self.cache_stats['misses'] += 1
        elif operation == 'write':
            self.cache_stats['writes'] += 1
        elif operation == 'invalidation':
            self.cache_stats['invalidations'] += 1
    
    async def invalidate_cache(self, pattern: str = None):
        """失效缓存"""
        if pattern:
            await self.cache_manager.delete_pattern(pattern)
        else:
            model_pattern = f"dao:{self.model.__name__}:*"
            await self.cache_manager.delete_pattern(model_pattern)
        
        self._update_cache_stats('invalidation', 'manual')
    
    @cache_result(ttl=300)
    async def get_by_id(self, id_value: Any) -> Optional[ModelType]:
        """带缓存的ID查询"""
        return await super().get_by_id(id_value)
    
    @cache_result(ttl=600)
    async def get_by_field(self, field_name: str, field_value: Any) -> Optional[ModelType]:
        """带缓存的字段查询"""
        return await super().get_by_field(field_name, field_value)
    
    @cache_result(ttl=300)
    async def get_all(
        self,
        limit: Optional[int] = None,
        offset: Optional[int] = None,
        order_by: Optional[str] = None,
        desc_order: bool = False
    ) -> List[ModelType]:
        """带缓存的全量查询"""
        return await super().get_all(limit, offset, order_by, desc_order)
    
    async def create(self, **kwargs) -> ModelType:
        """创建并更新缓存"""
        result = await super().create(**kwargs)
        
        if self.cache_config.enable_write_through:
            # 写穿透：同时更新缓存
            entity_key = CacheKeyGenerator.generate_entity_key(
                self.model.__name__, 
                getattr(result, 'id', None)
            )
            await self.cache_manager.set(
                entity_key,
                self._serialize_result(result),
                ttl=self.cache_config.default_ttl
            )
            
            # 失效相关查询缓存
            await self.invalidate_cache(f"dao:{self.model.__name__}:get_*")
        
        self._update_cache_stats('write', 'create')
        return result
    
    async def update_by_id(self, id_value: Any, **kwargs) -> Optional[ModelType]:
        """更新并失效缓存"""
        result = await super().update_by_id(id_value, **kwargs)
        
        if result:
            # 失效实体缓存
            entity_key = CacheKeyGenerator.generate_entity_key(
                self.model.__name__, 
                id_value
            )
            await self.cache_manager.delete(entity_key)
            
            # 失效相关查询缓存
            await self.invalidate_cache(f"dao:{self.model.__name__}:*")
        
        self._update_cache_stats('invalidation', 'update')
        return result
    
    async def delete_by_id(self, id_value: Any) -> bool:
        """删除并失效缓存"""
        result = await super().delete_by_id(id_value)
        
        if result:
            # 失效实体缓存
            entity_key = CacheKeyGenerator.generate_entity_key(
                self.model.__name__, 
                id_value
            )
            await self.cache_manager.delete(entity_key)
            
            # 失效相关查询缓存
            await self.invalidate_cache(f"dao:{self.model.__name__}:*")
        
        self._update_cache_stats('invalidation', 'delete')
        return result
    
    def get_cache_stats(self) -> Dict[str, Any]:
        """获取缓存统计"""
        total_requests = self.cache_stats['hits'] + self.cache_stats['misses']
        hit_rate = (self.cache_stats['hits'] / total_requests * 100) if total_requests > 0 else 0
        
        return {
            'model': self.model.__name__,
            'hits': self.cache_stats['hits'],
            'misses': self.cache_stats['misses'],
            'writes': self.cache_stats['writes'],
            'invalidations': self.cache_stats['invalidations'],
            'hit_rate': round(hit_rate, 2),
            'total_requests': total_requests
        }


class CachedFinancialDataDAO(CachedBaseDAO[ModelType], FinancialDataDAO[ModelType]):
    """带缓存的金融数据DAO"""
    
    def __init__(
        self,
        session: AsyncSession,
        model_class: Type[ModelType],
        cache_manager: CacheManager,
        cache_config: CachedDAOConfig = None
    ):
        CachedBaseDAO.__init__(self, session, model_class, cache_manager, cache_config)
        # 不调用FinancialDataDAO的__init__，因为它继承自BaseDAO
    
    @cache_result(ttl=600)
    async def get_by_symbol(self, symbol: str) -> List[ModelType]:
        """带缓存的股票代码查询"""
        return await FinancialDataDAO.get_by_symbol(self, symbol)
    
    @cache_result(ttl=300)
    async def get_by_date_range(
        self,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None,
        symbol: Optional[str] = None,
        limit: int = 1000
    ) -> List[ModelType]:
        """带缓存的日期范围查询"""
        return await FinancialDataDAO.get_by_date_range(
            self, start_date, end_date, symbol
        )
    
    @cache_result(ttl=60)  # 最新数据缓存时间较短
    async def get_latest_by_symbol(self, symbol: str, limit: int = 1) -> List[ModelType]:
        """带缓存的最新数据查询"""
        return await FinancialDataDAO.get_latest_by_symbol(self, symbol, limit)


class CachedDAOFactory:
    """缓存DAO工厂"""
    
    def __init__(self, cache_manager: CacheManager):
        self.cache_manager = cache_manager
        self.default_config = CachedDAOConfig()
    
    def create_cached_dao(
        self,
        session: AsyncSession,
        model_class: Type[ModelType],
        cache_config: CachedDAOConfig = None
    ) -> CachedBaseDAO[ModelType]:
        """创建缓存DAO实例"""
        config = cache_config or self.default_config
        
        # 检查是否为金融数据模型
        if hasattr(model_class, '__table__') and any(
            col.name in ['symbol', 'stock_code', 'bond_code', 'fund_code', 'trade_date']
            for col in model_class.__table__.columns
        ):
            return CachedFinancialDataDAO(session, model_class, self.cache_manager, config)
        else:
            return CachedBaseDAO(session, model_class, self.cache_manager, config)
    
    def create_financial_dao(
        self,
        session: AsyncSession,
        model_class: Type[ModelType],
        cache_config: CachedDAOConfig = None
    ) -> CachedFinancialDataDAO[ModelType]:
        """创建金融数据缓存DAO实例"""
        config = cache_config or self.default_config
        return CachedFinancialDataDAO(session, model_class, self.cache_manager, config)


# 全局缓存DAO工厂实例
_cached_dao_factory: Optional[CachedDAOFactory] = None

def get_cached_dao_factory() -> CachedDAOFactory:
    """获取全局缓存DAO工厂实例"""
    global _cached_dao_factory
    if _cached_dao_factory is None:
        from ..core.cache import get_cache_manager
        cache_manager = get_cache_manager()
        _cached_dao_factory = CachedDAOFactory(cache_manager)
    return _cached_dao_factory


def create_cached_dao_config(
    model_name: str,
    ttl: int = None,
    cache_levels: List[CacheLevel] = None,
    enable_write_through: bool = True
) -> CachedDAOConfig:
    """为特定模型创建缓存配置"""
    settings = get_settings()
    
    # 根据模型类型设置不同的缓存策略
    if model_name.endswith('DailyData'):
        # 日线数据：较长TTL，启用所有缓存级别
        return CachedDAOConfig(
            default_ttl=ttl or 1800,  # 30分钟
            cache_levels=cache_levels or [CacheLevel.L1_MEMORY, CacheLevel.L2_REDIS],
            cache_strategy=CacheStrategy.LRU,
            enable_write_through=enable_write_through,
            cache_key_prefix=f"daily_{model_name.lower()}"
        )
    elif model_name.endswith('BasicInfo'):
        # 基本信息：很长TTL，主要使用内存缓存
        return CachedDAOConfig(
            default_ttl=ttl or 3600,  # 1小时
            cache_levels=cache_levels or [CacheLevel.L1_MEMORY],
            cache_strategy=CacheStrategy.LFU,
            enable_write_through=enable_write_through,
            cache_key_prefix=f"basic_{model_name.lower()}"
        )
    elif 'News' in model_name:
        # 新闻数据：较短TTL，快速失效
        return CachedDAOConfig(
            default_ttl=ttl or 300,  # 5分钟
            cache_levels=cache_levels or [CacheLevel.L1_MEMORY, CacheLevel.L2_REDIS],
            cache_strategy=CacheStrategy.TTL,
            enable_write_through=enable_write_through,
            cache_key_prefix=f"news_{model_name.lower()}"
        )
    else:
        # 默认配置
        return CachedDAOConfig(
            default_ttl=ttl or 600,  # 10分钟
            cache_levels=cache_levels or [CacheLevel.L1_MEMORY, CacheLevel.L2_REDIS],
            cache_strategy=CacheStrategy.LRU,
            enable_write_through=enable_write_through,
            cache_key_prefix=f"default_{model_name.lower()}"
        )


# 为了向后兼容，提供CachedDAO别名
CachedDAO = CachedBaseDAO